/* TOKENS */
%{
#include "frontend.h"
#include "symboltable.h"

#define VAR -1			// symbol table entries with -1 parameters indicate variables

struct Env * top;
int in_begin;			// whether we are currently in the begin block
FILE * file;			// current output file
char* G_type;			// stores type for variable declaration list
struct myEntry * fn;	// current function entry
int brace_counter;		// nested block statement counter
int in_fn_decl;
int param_count;

long pos;				// indicates position in file where current degree/pair starts
long exp_start;			// indicates position in file where current expression starts
long exp_comp;
long exp_dot;

void unroll_composition(long exp_end);
%}

%token INT_CONST FLT_CONST STR_CONST DGR_CONST PAR_CONST ID TRUE_OP FALSE_OP NULL_OP

%token ATTRIBUTE

%token AND OR
%token ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN TRS_ASSIGN ROT_ASSIGN
%token EQ NE LE GE
%token T_BOOL T_INT T_FLOAT T_STRING T_IMAGE T_PAIR T_DEGREE T_VOID

%token IF ELSE WHILE FOR RETURN BREAK
%token BEGIN_SIGN END_SIGN DONE

%left '=' 
%left ADD_ASSIGN SUB_ASSIGN
%left MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN TRS_ASSIGN ROT_ASSIGN
%left ':'
%left AND OR
%left EQ NE LE GE '<' '>'
%left '+' '-' 
%left '/' '*' '%' '@' '#'
%right '!' NEG

%locations

%start program
%%
program
	: 								{top=NULL;
									 in_begin=0;
									 in_fn_decl=0;
									 new_Env();
									 brace_counter = 0;
									 manual_Add_Entry("compose","imageTypes",2);
									 manual_Add_Entry("canvas","imageTypes",2);
									 manual_Add_Entry("open","imageTypes",1);
									 manual_Add_Entry("save","intTypes",2);
									 manual_Add_Entry("print","intTypes",1);
									 manual_Add_Entry("show","intTypes",1);
									 manual_Add_Entry("charcoal","imageTypes",2);
									 manual_Add_Entry("blur","imageTypes",3);
									 manual_Add_Entry("sharpen","imageTypes",3);
									 manual_Add_Entry("gray","imageTypes",1);
									 manual_Add_Entry("crop","imageTypes",3);
									 manual_Add_Entry("convolve","imageTypes",10);
									 manual_Add_Entry("label","imageTypes",6);
									 manual_Add_Entry("normalize","imageTypes",1);
									 manual_Add_Entry("noise","imageTypes",1);
									 file = fopen("fn_decls.cxx","w+");}
	  fn_decls						{fclose(file); file = fopen("begin_block.cxx","w+");}
	  begin_block					{fclose(file); file = fopen("body_block.cxx","w+");}
	  body_block					{fclose(file); file = fopen("end_block.cxx","w+");}
	  end_block						{fclose(file); prev_Env();}
	;

begin_block
	: BEGIN_SIGN					{in_begin=1;
									 exp_start=ftell(file);}
	  statement						{in_begin=0;}
	;
body_block
	:								{new_Env();
									 manual_Add_Entry("_dollar_sign","imageTypes",VAR);
									 exp_start=ftell(file);}
	  statement						{prev_Env();}
	| /* empty */
	;
end_block
	: END_SIGN						{exp_start=ftell(file);}
	  statement						{prev_Env();}
	;

fn_decls
	: /* empty */
	| fn_decl						{fprintf(file,"\n");}
	  fn_decls
	;

fn_decl
	: type_spec	ID '('				{fn = add_Entry($2, $1, 0, @2);
									 new_Env();
									 in_fn_decl=1;
									 fprintf(file,"%s %s(", $1, $2);}
	  params ')'					{fn = NULL; fprintf(file,") ");
									 exp_start=ftell(file);}
	  statement						{in_fn_decl=0;
									 prev_Env();}
	;

type_spec
	: T_VOID						{$$ = "voidTypes";}
	| T_BOOL						{$$ = "boolTypes";}
	| T_INT							{$$ = "intTypes";}
	| T_FLOAT						{$$ = "floatTypes";}
	| T_STRING						{$$ = "stringTypes";}
	| T_IMAGE						{$$ = "imageTypes";}
	| T_PAIR						{$$ = "pairTypes";}
	| T_DEGREE						{$$ = "degreeTypes";}
	;

params
	: param_list
	| /* empty */
	;
param_list
	: param_list ',' type_spec ID	{if (fn != NULL) fn->params++;
									 add_Entry($4, $3, VAR, @4);
									 fprintf(file,", %s %s", $3, $4);}
	| type_spec ID					{if (fn != NULL) fn->params++;
									 add_Entry($2, $1, VAR, @2);
									 fprintf(file,"%s %s", $1, $2);}
	;

/**
 * STATEMENT
 */

statement
	: '{'							{if (in_fn_decl == 0)
										new_Env();
									 if ( brace_counter != 0 || in_fn_decl == 1)
										fprintf(file,"{\n");
									 brace_counter++;
									 exp_start=ftell(file);}
	  statement_list '}'			{//pretty_print_Env_Stack("\t", top);
									 if (in_begin==0 && in_fn_decl==0)
										prev_Env();
									 brace_counter--;
									 if (brace_counter != 0 || in_fn_decl == 1)
										fprintf(file,"\n}");
									 else
										fprintf(file,"\n");}

	| expression ';'				{fprintf(file,";");}

	| var_decl ','					{fprintf(file,", ");}
	  var_decl_opt ';'				{fprintf(file,";");}

	| IF '('						{fprintf(file,"if (");
									 exp_start=ftell(file);}
	  expression ')'				{fprintf(file,") ");
									 exp_start=ftell(file);}
	  statement else_opt

	| WHILE '('						{fprintf(file,"while (");
									 exp_start=ftell(file);}
	  expression ')'				{fprintf(file,") ");
									 exp_start=ftell(file);}
	  statement

	| FOR '(' 						{fprintf(file,"for (");
									 exp_start=ftell(file);}
	  expression_opt ';' 			{fprintf(file,";");
									 exp_start=ftell(file);}
	  expression_opt ';' 			{fprintf(file,";");
									 exp_start=ftell(file);}
	  expression_opt ')' 			{fprintf(file,") ");
									 exp_start=ftell(file);}
	  statement

	| RETURN ';'					{fprintf(file,"return ;");}

	| RETURN						{fprintf(file,"return ");
									 exp_start=ftell(file);}
	  expression ';'				{fprintf(file,";");}

	| BREAK ';'						{fprintf(file,"break ;");}
	;

var_decl_opt
	: ID							{add_Entry($1, G_type, VAR, @1);
									 fprintf(file,"%s", $1);}
	  var_decl_opt2
	| ID '='						{add_Entry($1, G_type, VAR, @1);
									 fprintf(file,"%s = ", $1);
									 exp_start=ftell(file);}
	  expression
	  var_decl_opt2
	;

var_decl_opt2
	: /* empty */
	| ','							{fprintf(file,", ");}
	  var_decl_opt
	;

var_decl
	: type_spec ID					{add_Entry($2, $1, VAR, @2);
									 G_type = $1;
									 fprintf(file,"%s %s", $1, $2);}
	| type_spec ID '='				{add_Entry($2, $1, VAR, @2);
									 G_type = $1;
									 fprintf(file,"%s %s = ", $1, $2);
									 exp_start=ftell(file);}
	  expression
	;

statement_list
	: /* empty */
	| statement						{fprintf(file,"\n");
									 exp_start=ftell(file);}
	  statement_list
	;

else_opt
	: /*empty*/
	| ELSE							{fprintf(file,"\nelse ");
									 exp_start=ftell(file);}
	  statement
	  ;

/**
 * EXPRESSSION
  */

expression
	: const

	| ID							{check_Entry($1, VAR, @1);
									 fprintf(file,"%s", $1);
									 free($1);}

	| ID '.'						{check_Entry($1, VAR, @1);
									 fprintf(file,"%s.", $1);
									 free($1);}	  
	  ATTRIBUTE						{fprintf(file,"%s", $4);
	  								 if (strcmp($4,"height") == 0
									 	|| strcmp($4,"width") == 0)
										fprintf(file,"()");}
	  assign_opt

	| ID '('						{fprintf(file,"%s(", $1);}
	  exp_list_opt ')'				{check_Entry($1, $4, @1);
									 free($1);
									 fprintf(file,")");}

	| ID '='						{check_Entry($1, VAR, @1);
									 fprintf(file,"%s = ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression					

	| ID ADD_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s += ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID SUB_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s -= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID MUL_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s *= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID DIV_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s /= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID MOD_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s %%= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID TRS_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s #= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| ID ROT_ASSIGN					{check_Entry($1, VAR, @1);
									 fprintf(file,"%s @= ", $1);
									 exp_start=ftell(file);
									 free($1);}
	  expression

	| expression AND				{fprintf(file,"&&");}
	  expression

	| expression OR					{fprintf(file,"||");}
	  expression

	| expression LE					{fprintf(file,"<=");}
	  expression

	| expression GE					{fprintf(file,">=");}
	  expression

	| expression EQ					{fprintf(file,"==");}
	  expression

	| expression NE					{fprintf(file,"!=");}
	  expression

	| expression '<'				{fprintf(file,"<");}
	  expression

	| expression '>'				{fprintf(file,">");}
	  expression

	| expression '@'				{fprintf(file,"&");}
	  expression

	| expression '#'				{fprintf(file,"|");}
	  expression

	| expression '+'				{fprintf(file,"+");}
	  expression

	| expression '-'				{fprintf(file,"-");}
	  expression

	| expression '/'				{fprintf(file,"/");}
	  expression

	| expression '*'				{fprintf(file,"*");}
	  expression

	| expression '%'				{fprintf(file,"%%");}
	  expression

	| expression ':'				{exp_comp=ftell(file);
									 fprintf(file,":");}
	  partial_fn					{unroll_composition(ftell(file)-1);}

	| '!' 							{fprintf(file,"!");}
	  expression

	| '-'							{fprintf(file,"-");}
	  expression %prec NEG

	| '('							{fprintf(file,"(");}
	  exp_list
	  ')'							{fprintf(file,")");}

	| '['							{pos = ftell(file);
									 fprintf(file,"           (");}
	  expression
	  degree_opt
	  ']'							{fprintf(file,")");}

	| var_decl
	;

degree_opt
	: /*empty*/						{fseek(file, pos, SEEK_SET);
									 fprintf(file,"degreeTypes");
									 fseek(file, 0, SEEK_END);}
	| ','							{fseek(file, pos, SEEK_SET);
									 fprintf(file,"pairTypes");
									 fseek(file, 0, SEEK_END);
									 fprintf(file,",");}
	  expression
	;

exp_list
	: expression					{$$ = 1;}
	| expression ','				{fprintf(file,",");}
	  exp_list						{$$ = $4 + 1;}
	;

expression_opt_1
	: /*empty*/
	| expression ','				{fprintf(file,",");
									 param_count++;}
	  expression_opt_1
	;
expression_opt_2
	: /*empty*/
	| ','							{fprintf(file,",");}
	  expression					{param_count++;}
	  expression_opt_2
	;
expression_opt
	: /*empty*/
	| expression
	;
exp_list_opt
	: exp_list						{$$ = $1;}
	| /* empty */					{$$ = 0;}
	;

partial_fn	
	: ID							{fprintf(file,"%s", $1);}
	  '('							{fprintf(file,"(");
	  								 param_count=0;}
	  expression_opt_1 '.'			{exp_dot=ftell(file);
									 fprintf(file,".");}
	  expression_opt_2 ')'			{fprintf(file,")");
	  								 check_Entry($1, param_count+1, @1);
									 param_count=0;}
	;

const
	: TRUE_OP						{fprintf(file,"boolTypes(true)");}
	| FALSE_OP						{fprintf(file,"boolTypes(false)");}
	| NULL_OP						{fprintf(file,"imageTypes(null)");}
	| INT_CONST						{fprintf(file,"intTypes(%s)", $1); free($1);}
	| FLT_CONST						{fprintf(file,"floatTypes(%s)", $1); free($1);}
	| STR_CONST						{fprintf(file,"stringTypes(%s)", $1); free($1);}
	| PAR_CONST						{unsigned int i, len;
									 char* s=$1;
									 len = strlen(s);
									 fprintf(file,"pairTypes(");
									 for(i=1; i < len; i++)
										if ((s[i] != '[') && (s[i] != ']'))
											fprintf(file, "%c", s[i]);
									 fprintf(file,")");
									 free($1);}

	| DGR_CONST						{unsigned int i, len;
									 char* s=$1;
									 len = strlen(s);
									 fprintf(file,"degreeTypes(");
									 for(i=1; i < len; i++)
											if ((s[i] != '[') && (s[i] != ']'))
												fprintf(file, "%c", s[i]);
									 fprintf(file,")");
									 free($1);}
	;

assign_opt
	: /*empty*/
	| '='							{fprintf(file, " = ");}
	  expression
	;
%%

int lyyerror(YYLTYPE t, char *s, ...)
{
	va_list ap;
	va_start(ap, s);

	fprintf(stderr, "%s %d.%d-%d.%d: error: ",
	t.file_name,
	t.first_line, t.first_column,
	t.last_line, t.last_column);
	vfprintf(stderr, s, ap);
	fprintf(stderr, "\n");
	ret = 1;
	return 0;
}

void unroll_composition(long exp_end)
{
	char *expr = (char *)malloc((exp_comp-exp_start+1)*sizeof(char));
	char *lfn = (char *)malloc((exp_dot-exp_comp)*sizeof(char));
	char *rfn = (char *)malloc((exp_end-exp_dot+1)*sizeof(char));
		
	fseek(file,exp_start,SEEK_SET);
	fgets(expr,(exp_comp-exp_start+1),file);
	fseek(file,exp_comp+1,SEEK_SET);
	fgets(lfn,(exp_dot-exp_comp),file);
	fseek(file,exp_dot+1,SEEK_SET);
	fgets(rfn,(exp_end-exp_dot+1),file);

	fseek(file,exp_start,SEEK_SET);
	fprintf(file,"%s%s%s", lfn, expr, rfn);
	free(expr);
	free(lfn);
	free(rfn);
}
